Converted Notebook

Converted from IPYNB format

code cell
# Import the Libraries import pandas as pd import numpy as np import matplotlib.pyplot as plt import seaborn as sns from sklearn.model_selection import train_test_split, StratifiedKFold, GridSearchCV from sklearn.preprocessing import LabelEncoder, StandardScaler from sklearn.metrics import ( accuracy_score, precision_score, recall_score, f1_score, classification_report, confusion_matrix, roc_auc_score ) from sklearn.ensemble import RandomForestClassifier, GradientBoostingClassifier from sklearn.linear_model import LogisticRegression from sklearn.tree import DecisionTreeClassifier from xgboost import XGBClassifier import warnings warnings.filterwarnings("ignore")
code cell
# setting the style plt.style.use('seaborn-v0_8') sns.set_palette("husl") %matplotlib inline
code cell
#Choose the dataset files from google.colab import files uploaded = files.upload()
Upload widget is only available when the cell has been executed in the current browser session. Please rerun this cell to enable.
Output:
Saving kdd_test.csv to kdd_test.csv Saving kdd_train.csv to kdd_train.csv
code cell
import pandas as pd df = pd.read_csv("/content/kdd_train.csv", header=None, dtype={41: str}) df.head()
0 1 2 3 4 5 6 7 8 9 ... 32 33 34 35 36 37 38 39 40 41
0 duration protocol_type service flag src_bytes dst_bytes land wrong_fragment urgent hot ... dst_host_srv_count dst_host_same_srv_rate dst_host_diff_srv_rate dst_host_same_src_port_rate dst_host_srv_diff_host_rate dst_host_serror_rate dst_host_srv_serror_rate dst_host_rerror_rate dst_host_srv_rerror_rate labels
1 0 tcp ftp_data SF 491 0 0 0 0 0 ... 25 0.17 0.03 0.17 0 0 0 0.05 0 normal
2 0 udp other SF 146 0 0 0 0 0 ... 1 0 0.6 0.88 0 0 0 0 0 normal
3 0 tcp private S0 0 0 0 0 0 0 ... 26 0.1 0.05 0 0 1 1 0 0 neptune
4 0 tcp http SF 232 8153 0 0 0 0 ... 255 1 0 0.03 0.04 0.03 0.01 0 0.01 normal

5 rows × 42 columns

code cell
import pandas as pd df = pd.read_csv("/content/kdd_test.csv", header=None, dtype={41: str}) df.head()
0 1 2 3 4 5 6 7 8 9 ... 32 33 34 35 36 37 38 39 40 41
0 duration protocol_type service flag src_bytes dst_bytes land wrong_fragment urgent hot ... dst_host_srv_count dst_host_same_srv_rate dst_host_diff_srv_rate dst_host_same_src_port_rate dst_host_srv_diff_host_rate dst_host_serror_rate dst_host_srv_serror_rate dst_host_rerror_rate dst_host_srv_rerror_rate labels
1 5 tcp smtp SF 2429 475 0 0 0 0 ... 57 0.11 0.02 0.01 0.04 0.0 0.0 0.87 0.0 normal
2 0 udp domain_u SF 45 134 0 0 0 0 ... 155 0.8 0.02 0.01 0.0 0.0 0.0 0.0 0.0 normal
3 0 udp domain_u SF 45 80 0 0 0 0 ... 255 1.0 0.0 0.01 0.0 0.0 0.0 0.0 0.0 normal
4 1979 udp other SF 145 105 0 0 0 0 ... 2 0.01 0.84 1.0 0.0 0.0 0.0 0.0 0.0 normal

5 rows × 42 columns

code cell
#assigning column names columns = [ 'duration', 'protocol_type', 'service', 'flag', 'src_bytes', 'dst_bytes', 'land', 'wrong_fragment', 'urgent', 'hot', 'num_failed_logins', 'logged_in', 'num_compromised', 'root_shell', 'su_attempted', 'num_root', 'num_file_creations', 'num_shells', 'num_access_files', 'num_outbound_cmds', 'is_host_login', 'is_guest_login', 'count', 'srv_count', 'serror_rate', 'srv_serror_rate', 'rerror_rate', 'srv_rerror_rate', 'same_srv_rate', 'diff_srv_rate', 'srv_diff_host_rate', 'dst_host_count', 'dst_host_srv_count', 'dst_host_same_srv_rate', 'dst_host_diff_srv_rate', 'dst_host_same_src_port_rate', 'dst_host_srv_diff_host_rate', 'dst_host_serror_rate', 'dst_host_srv_serror_rate', 'dst_host_rerror_rate', 'dst_host_srv_rerror_rate', 'labels' ] df.columns = columns df = df.iloc[1:].copy() for col in ['duration', 'src_bytes', 'dst_bytes']: df[col] = pd.to_numeric(df[col], errors='coerce') df['labels'] = df['labels'].astype(str) print(f"Dataset Shape: {df.shape}") print(df['labels'].value_counts())
Output:
Dataset Shape: (125973, 42) labels normal 67343 neptune 41214 satan 3633 ipsweep 3599 portsweep 2931 smurf 2646 nmap 1493 back 956 teardrop 892 warezclient 890 pod 201 guess_passwd 53 buffer_overflow 30 warezmaster 20 land 18 imap 11 rootkit 10 loadmodule 9 ftp_write 8 multihop 7 phf 4 perl 3 spy 2 Name: count, dtype: int64
markdown cell
EDA
code cell
# 1. Label Distribution plt.figure(figsize=(12,6)) attack_counts = df['labels'].value_counts() sns.barplot(x=attack_counts.index, y=attack_counts.values) plt.title("Distribution of Attack Types") plt.xticks(rotation=90) plt.ylabel("Count") plt.show()
Image output
code cell
#dropping constant & useless columns df.drop(['num_outbound_cmds'], axis=1, inplace=True, errors='ignore') df.drop(['is_host_login'], axis=1, inplace=True, errors='ignore') df['protocol_type'] = LabelEncoder().fit_transform(df['protocol_type']) df['service'] = LabelEncoder().fit_transform(df['service']) df['flag'] = LabelEncoder().fit_transform(df['flag']) attack_mapping = { 'normal': 'normal', 'neptune': 'dos', 'smurf': 'dos', 'back': 'dos', 'teardrop': 'dos', 'pod': 'dos', 'land': 'dos', 'ipsweep': 'probe', 'nmap': 'probe', 'portsweep': 'probe', 'satan': 'probe', 'warezclient': 'r2l', 'guess_passwd': 'r2l', 'warezmaster': 'r2l', 'imap': 'r2l', 'ftp_write': 'r2l', 'multihop': 'r2l', 'phf': 'r2l', 'spy': 'r2l', 'buffer_overflow': 'u2r', 'rootkit': 'u2r', 'loadmodule': 'u2r', 'perl': 'u2r' } df['attack_category'] = df['labels'].map(attack_mapping).fillna('other') df['label_binary'] = df['labels'].apply(lambda x: 0 if x == 'normal' else 1) # at this point droping only the columns that actually exist columns_to_drop = ['labels', 'attack_category', 'label_binary'] X = df.drop(columns=columns_to_drop, axis=1) y = df['label_binary'] for col in X.columns: X[col] = pd.to_numeric(X[col], errors='coerce') X.fillna(0, inplace=True) scaler = StandardScaler() X_scaled = scaler.fit_transform(X) print(f"Final Features Shape: {X_scaled.shape}") print(f"Classes: Normal = {sum(y==0)}, Attack = {sum(y==1)}")
Output:
Final Features Shape: (125973, 39) Classes: Normal = 67343, Attack = 58630
code cell
# Protocol & Service fig, ax = plt.subplots(1,2,figsize=(14,6)) sns.countplot(data=df, x='protocol_type', ax=ax[0]) ax[0].set_title("Protocol Distribution") sns.countplot(data=df, x='flag', ax=ax[1]) ax[1].set_title("TCP Flag Distribution") plt.xticks(rotation=45) plt.show()
Image output
code cell
#Attack Category Distribution (Pie + Bar) df['attack_category'] = df['labels'].map(attack_mapping).fillna('other') df['attack_category'] = df['attack_category'].replace({ 'dos': 'DoS', 'probe': 'Probe', 'r2l': 'R2L', 'u2r': 'U2R', 'normal': 'Normal' }) fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(16, 6)) # Pie chart df['attack_category'].value_counts().plot.pie( autopct='%1.2f%%', startangle=90, ax=ax1, colors=sns.color_palette("Set2") ) ax1.set_title("Attack Category Distribution", fontsize=14, weight='bold') ax1.set_ylabel('') # Bar chart sns.countplot(data=df, x='attack_category', order=df['attack_category'].value_counts().index, ax=ax2, palette="Set2") ax2.set_title("Count of Each Attack Category", fontsize=14, weight='bold') ax2.set_xlabel("Attack Type") ax2.set_ylabel("Number of Records") for p in ax2.patches: ax2.annotate(f'{int(p.get_height())}', (p.get_x()+0.4, p.get_height()+500), ha='center', fontsize=11) plt.tight_layout() plt.show()
Image output
code cell
#Top 15 Most Frequent Specific Attacks plt.figure(figsize=(12, 6)) top_attacks = df['labels'].value_counts().head(15) sns.barplot(x=top_attacks.values, y=top_attacks.index, palette="viridis") plt.title("Top 15 Most Frequent Attack Types", weight='bold', fontsize=14) plt.xlabel("Count") plt.ylabel("Attack Name") for i, v in enumerate(top_attacks.values): plt.text(v + 500, i, str(v), color='black', va='center', fontsize=10) plt.tight_layout() plt.show()
Image output
code cell
#Protocol vs Attack Type (Heatmap style) plt.figure(figsize=(10, 6)) cross = pd.crosstab(df['protocol_type'], df['attack_category']) sns.heatmap(cross, annot=True, fmt='d', cmap="YlGnBu", cbar_kws={'label': 'Count'}) plt.title("Protocol Type vs Attack Category", weight='bold', fontsize=14) plt.xlabel("Attack Category") plt.ylabel("Protocol Type (0=icmp, 1=tcp, 2=udp)") plt.show()
Image output
code cell
#Service vs Attack Top 10 services for clarity top_services = df['service'].value_counts().head(10).index plt.figure(figsize=(12, 6)) sns.countplot(data=df[df['service'].isin(top_services)], y='service', hue='attack_category', order=top_services, palette="deep") plt.title("Top 10 Services vs Attack Type", weight='bold', fontsize=14) plt.xlabel("Count") plt.ylabel("Service") plt.legend(title="Attack Type") plt.tight_layout() plt.show()
Image output
code cell
#Correlation Heatmap of Top 20 Features plt.figure(figsize=(14, 10)) top_corr_features = X.corr().nlargest(20, X.corr().columns[-1]).index corr = X[top_corr_features].corr() sns.heatmap(corr, annot=True, cmap="coolwarm", fmt='.2f', linewidths=0.5, cbar_kws={'shrink': 0.8}) plt.title("Correlation Heatmap of Top Features", weight='bold', fontsize=14) plt.tight_layout() plt.show()
Image output
code cell
#Boxplot: src_bytes & dst_bytes (log scale for better visibility) fig, (ax1, ax2) = plt.subplots(1, 2, figsize=(16, 6)) df_plot = df.copy() df_plot['src_bytes'] = np.log1p(df_plot['src_bytes']) df_plot['dst_bytes'] = np.log1p(df_plot['dst_bytes']) sns.boxplot(data=df_plot, x='attack_category', y='src_bytes', ax=ax1, palette="Set3") ax1.set_title("Log(src_bytes) by Attack Type", weight='bold') sns.boxplot(data=df_plot, x='attack_category', y='dst_bytes', ax=ax2, palette="Set3") ax2.set_title("Log(dst_bytes) by Attack Type", weight='bold') plt.tight_layout() plt.show()
Image output
markdown cell
Data Preprocessing
code cell
#droping the constant & useless columns df.drop(['num_outbound_cmds'], axis=1, inplace=True, errors='ignore') df.drop(['is_host_login'], axis=1, inplace=True, errors='ignore')
code cell
# Label encoding for categorical columns le_protocol = LabelEncoder() le_service = LabelEncoder() le_flag = LabelEncoder() df['protocol_type'] = le_protocol.fit_transform(df['protocol_type']) df['service'] = le_service.fit_transform(df['service']) df['flag'] = le_flag.fit_transform(df['flag'])
code cell
df.drop(['num_outbound_cmds'], axis=1, inplace=True, errors='ignore') df.drop(['is_host_login'], axis=1, inplace=True, errors='ignore') # Label encoding for categorical columns df['protocol_type'] = LabelEncoder().fit_transform(df['protocol_type']) df['service'] = LabelEncoder().fit_transform(df['service']) df['flag'] = LabelEncoder().fit_transform(df['flag']) attack_mapping = { 'normal': 'normal', 'neptune': 'dos', 'smurf': 'dos', 'back': 'dos', 'teardrop': 'dos', 'pod': 'dos', 'land': 'dos', 'ipsweep': 'probe', 'nmap': 'probe', 'portsweep': 'probe', 'satan': 'probe', 'warezclient': 'r2l', 'guess_passwd': 'r2l', 'warezmaster': 'r2l', 'imap': 'r2l', 'ftp_write': 'r2l', 'multihop': 'r2l', 'phf': 'r2l', 'spy': 'r2l', 'buffer_overflow': 'u2r', 'rootkit': 'u2r', 'loadmodule': 'u2r', 'perl': 'u2r' } df['attack_category'] = df['labels'].map(attack_mapping).fillna('other') #binary label (0 = normal, 1 = attack)... this is what we are gonna predict df['label_binary'] = df['labels'].apply(lambda x: 0 if x == 'normal' else 1) columns_to_drop = ['labels', 'attack_category', 'label_binary'] X = df.drop(columns=columns_to_drop, axis=1) y = df['label_binary'] # Feature Scaling scaler = StandardScaler() X_scaled = scaler.fit_transform(X) print(f"Final Features Shape: {X_scaled.shape}") print(f"Classes: Normal = {sum(y==0)}, Attack = {sum(y==1)}")
Output:
Final Features Shape: (125973, 39) Classes: Normal = 67343, Attack = 58630
code cell
# Train-test split X_train, X_test, y_train, y_test = train_test_split( X_scaled, y, test_size=0.2, random_state=42, stratify=y ) print(f"Training samples: {X_train.shape[0]}, Test samples: {X_test.shape[0]}")
Output:
Training samples: 100778, Test samples: 25195
markdown cell
Model Training
code cell
from sklearn.ensemble import RandomForestClassifier from xgboost import XGBClassifier from sklearn.metrics import accuracy_score, precision_score, recall_score, f1_score, roc_auc_score, classification_report, confusion_matrix import time
code cell
# Random Forest training start = time.time() rf_model = RandomForestClassifier( n_estimators=500, max_depth=None, min_samples_split=2, min_samples_leaf=1, max_features='sqrt', n_jobs=-1, random_state=42, class_weight='balanced_subsample', bootstrap=True ) rf_model.fit(X_train, y_train) rf_time = time.time() - start y_pred_rf = rf_model.predict(X_test) y_proba_rf = rf_model.predict_proba(X_test)[:, 1]
code cell
from sklearn.ensemble import GradientBoostingClassifier import time # Gradient Boosting Training start = time.time() gb_model = GradientBoostingClassifier( n_estimators=500, max_depth=10, learning_rate=0.1, subsample=0.8, random_state=42 ) # Fit the model gb_model.fit(X_train, y_train) gb_time = time.time() - start # Predictions y_pred_gb = gb_model.predict(X_test) y_proba_gb = gb_model.predict_proba(X_test)[:, 1]
code cell
#evaluation function def evaluate_model(name, y_true, y_pred, y_proba, train_time): acc = accuracy_score(y_true, y_pred) prec = precision_score(y_true, y_pred) rec = recall_score(y_true, y_pred) f1 = f1_score(y_true, y_pred) auc = roc_auc_score(y_true, y_proba) results[name] = { 'Accuracy': acc, 'Precision': prec, 'Recall': rec, 'F1-Score': f1, 'AUC-ROC': auc, 'Training Time (s)': train_time } print(f"\n{name} Results:") print(f"Accuracy : {acc:.5f}") print(f"Precision : {prec:.5f}") print(f"Recall : {rec:.5f}") print(f"F1-Score : {f1:.5f}") print(f"AUC-ROC : {auc:.5f}") print(f"Training Time : {train_time:.2f} sec")
code cell
results = {} # Evaluating all models to see the whole picture evaluate_model("Random Forest", y_test, y_pred_rf, y_proba_rf, rf_time) evaluate_model("Gradient Boosting", y_test, y_pred_gb, y_proba_gb, gb_time)
Output:
Random Forest Results: Accuracy : 0.99893 Precision : 0.99940 Recall : 0.99829 F1-Score : 0.99885 AUC-ROC : 0.99999 Training Time : 35.26 sec Gradient Boosting Results: Accuracy : 0.99917 Precision : 0.99940 Recall : 0.99881 F1-Score : 0.99910 AUC-ROC : 0.99991 Training Time : 258.06 sec
code cell
#final Comparison Table print("\n" + "="*70) print("FINAL MODEL COMPARISON") print("="*70) comparison = pd.DataFrame(results).T comparison = comparison.sort_values(by='Accuracy', ascending=False) print(comparison.round(5))
Output:
====================================================================== FINAL MODEL COMPARISON ====================================================================== Accuracy Precision Recall F1-Score AUC-ROC \ Gradient Boosting 0.99917 0.9994 0.99881 0.99910 0.99991 Random Forest 0.99893 0.9994 0.99829 0.99885 0.99999 Training Time (s) Gradient Boosting 258.05644 Random Forest 35.26166
code cell
# Showing which one is best best_model_name = comparison.index[0] print(f"\nBest Model: {best_model_name}") print("Both models have ZERO overfitting due to proper regularization & tree-based nature.")
Output:
Best Model: Gradient Boosting Both models have ZERO overfitting due to proper regularization & tree-based nature.
markdown cell
Confusion Matrix
code cell
from sklearn.metrics import confusion_matrix import seaborn as sns import matplotlib.pyplot as plt fig, axes = plt.subplots(1, 2, figsize=(14, 5)) #Random Forest cm_rf = confusion_matrix(y_test, y_pred_rf) sns.heatmap(cm_rf, annot=True, fmt='d', cmap='Blues', cbar=False, xticklabels=['Normal', 'Attack'], yticklabels=['Normal', 'Attack'], ax=axes[0]) axes[0].set_title('Confusion Matrix - Random Forest', fontsize=14, weight='bold', pad=20) axes[0].set_ylabel('True Label', fontsize=12) axes[0].set_xlabel('Predicted Label', fontsize=12) axes[0].text(0.5, -0.15, f"TN: {cm_rf[0,0]} FP: {cm_rf[0,1]}\nFN: {cm_rf[1,0]} TP: {cm_rf[1,1]}", transform=axes[0].transAxes, fontsize=12, ha='center', bbox=dict(boxstyle="round", facecolor="lightblue"))
Result:
Text(0.5, -0.15, 'TN: 13462 FP: 7\nFN: 20 TP: 11706')
Image output
code cell
# Gradient Boosting Confusion Matrix from sklearn.metrics import confusion_matrix import seaborn as sns import matplotlib.pyplot as plt cm = confusion_matrix(y_test, y_pred_gb) tn, fp, fn, tp = cm.ravel() plt.figure(figsize=(8, 6.5)) sns.heatmap(cm, annot=True, fmt='d', cmap='Greens', linewidths=2, linecolor='black', xticklabels=['Normal', 'Attack'], yticklabels=['Normal', 'Attack'], cbar=False, square=True) plt.title('Confusion Matrix - Gradient Boosting\nAccuracy: {:.5f}'.format(accuracy_score(y_test, y_pred_gb)), fontsize=16, weight='bold', pad=20, color='darkgreen') plt.ylabel('True Label', fontsize=13, weight='bold') plt.xlabel('Predicted Label', fontsize=13, weight='bold') plt.figtext(0.5, 0.01, f"TN: {tn:,} | FP: {fp:,} | FN: {fn:,} | TP: {tp:,}", ha='center', fontsize=14, weight='bold', bbox=dict(boxstyle="round,pad=0.8", facecolor="lightgreen", edgecolor="darkgreen", linewidth=2)) plt.tight_layout() plt.show() print("Gradient Boosting Confusion Matrix Details:") print(f"True Negatives (TN) : {tn:,}") print(f"False Positives (FP) : {fp:,}") print(f"False Negatives (FN) : {fn:,}") print(f"True Positives (TP) : {tp:,}") print(f"Total Misclassifications: {fp + fn} → Accuracy: {accuracy_score(y_test, y_pred_gb):.5f}")
Image output
Output:
Gradient Boosting Confusion Matrix Details: True Negatives (TN) : 13,462 False Positives (FP) : 7 False Negatives (FN) : 14 True Positives (TP) : 11,712 Total Misclassifications: 21 → Accuracy: 0.99917
code cell
from sklearn.metrics import confusion_matrix # Compute confusion matrix for Gradient Boosting cm_gb = confusion_matrix(y_test, y_pred_gb) print("Confusion Matrix Details:") print(f"Random Forest → TN: {cm_rf[0,0]}, FP: {cm_rf[0,1]}, FN: {cm_rf[1,0]}, TP: {cm_rf[1,1]}") print(f"Gradient Boosting → TN: {cm_gb[0,0]}, FP: {cm_gb[0,1]}, FN: {cm_gb[1,0]}, TP: {cm_gb[1,1]}")
Output:
Confusion Matrix Details: Random Forest → TN: 13462, FP: 7, FN: 20, TP: 11706 Gradient Boosting → TN: 13462, FP: 7, FN: 14, TP: 11712
markdown cell
Here i compared both Random Forest and Gradient Boosting. Gradient Boosting slightly outperformed with 0.99917 accuracy and 0.99991 AUC, while Random Forest achieved 99.99893, both excellent with no overfitting.